#include <iostream>
#include <string>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#define MAXLINE 4096

using namespace std;

int main(int argc, char **argv)
{
    int listenfd, connfd;
    struct sockaddr_in servaddr;
    char buff[4096];
    int n;
    // 根据地址类型创建了一个TCP的socket，如果出错打印出错码
    // socket参数：(协议域如ipv4，socket类型如tcp，指定协议若0时自动选择)
    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        cout << "create socket error:" << strerror(errno) << "(errno:" << errno << ")" << endl;
        return 0;
    }
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    // INADDR_ANY一般指定系统中的0.0.0.0地址
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(6666);
    // 将地址绑定在listenfd上
    // bind参数：(socket描述字，一个const struct sockaddr*指针指向要绑定socketfd的协议地址)
    if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
    {
        cout << "bind socket error:" << strerror(errno) << "(errno:" << errno << ")" << endl;
        return 0;
    }
    // 监听这个listenfd，若客户端调用connect()发出请求，服务器端就会收到
    // listen参数：(要监听的socket描述字，相应socket可以排队的最大连接个数)
    if (listen(listenfd, 10) == -1)
    {
        cout << "listen socket error:" << strerror(errno) << "(errno:" << errno << ")" << endl;
        return 0;
    }
    cout << "======waiting for client's request======" << endl;
    // 在while循环中持续接收包，accpet和recv都在循环中，即收到包后fd关闭，下个包重新接收包
    while (1)
    {
        // TCP服务器监听到请求后，调用accept()函数，连接就建立好了
        // accpet参数：(服务器socket描述字，指向struct sockaddr*的指针用于返回客户端的协议地址，协议地址长度)
        // 若accpet成功，返回值是由内核自动生成的一个全新的描述字，代表与返回客户的TCP连接
        // accpet的第一个参数是服务器的监听socket描述字，只创建一个
        // accept函数返回的是已连接的socket描述字，内核为每个由服务器进程接受的客户创建了一个已连接的socket描述字，当服务器完成服务，相应的已连接socket描述字就被关闭
        if ((connfd = accept(listenfd, (struct sockaddr *)NULL, NULL)) == -1)
        {
            cout << "accept socket error:" << strerror(errno) << "(errno:" << errno << ")" << endl;
            continue;
        }
        // recv函数接收到的字符串不带"\0"结束符
        // recv参数：(已连接的socket描述字，缓冲区buff，缓冲区长度)
        n = recv(connfd, buff, MAXLINE, 0);
        buff[n] = '\0';
        cout << "recv msg from client: " << buff << endl;
        close(connfd);
    }
    // 完成读写操作后关闭相应的socket描述字，需要包含头文件<unistd.h>
    close(listenfd);
    return 0;
}
